package com.linfeng.component.mybatisplus.entity.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.linfeng.component.mybatisplus.entity.IEntity;
import com.linfeng.component.mybatisplus.event.cmd.CreateCmd;
import com.linfeng.component.mybatisplus.event.cmd.UpdateCmd;
import com.linfeng.component.mybatisplus.event.qry.PageQry;
import com.linfeng.component.mybatisplus.event.qry.Qry;
import com.linfeng.component.mybatisplus.tools.PageTools;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Data
public class Entity<D, P, G extends IService<P>> implements IEntity<D, P> {

    protected Class<D> dtoClass = this.currentDTOClass();
    protected Class<P> modelClass = this.currentModelClass();

    @Autowired
    protected G gateway;

    public <Q extends PageQry> IPage<D> page(Q q) {

        IPage<P> page = gateway.page(new Page<>(q.getPage(), q.getLimit()), convert(q));

        return PageTools.convertPage(page, dtoClass);
    }

    public D getById(Serializable id) {

        return BeanUtil.copyProperties(gateway.getById(id), dtoClass);
    }

    public D getOne(QueryWrapper<P> queryWrapper) {

        return BeanUtil.copyProperties(gateway.getOne(queryWrapper), dtoClass);
    }

    public D getOne(Map<String, Object> queryMap) {

        return getOne(convert(queryMap));
    }

    public List<D> list() {

        List<P> list = gateway.list();

        return BeanUtil.copyToList(list, dtoClass);
    }

    public List<D> list(Map<String, Object> map) {

        return list(convert(map));
    }

    public <Q extends Qry> List<D> list(Q q) {

        return list(convert(q));
    }

    public List<D> list(QueryWrapper<P> queryWrapper) {

        return BeanUtil.copyToList(gateway.list(queryWrapper), dtoClass);
    }

    public <C extends CreateCmd> boolean  create(C cmd) {

        P p = BeanUtil.copyProperties(cmd, modelClass);
        try {
            // 初始化创建时间，存在则初始化，否则跳过
            Method setCreatorId = modelClass.getMethod("setCreateTime", Date.class);
            setCreatorId.invoke(p, new Date());
        } catch (NoSuchMethodException e) {
            log.info("model类中不存在createTime属性");
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        return gateway.save(p);
    }

    public <C extends UpdateCmd> boolean  update(C cmd) {

        P p = BeanUtil.copyProperties(cmd, modelClass);
        try {
            // 初始化修改时间，存在则初始化，否则跳过
            Method setCreatorId = modelClass.getMethod("setUpdateTime", Date.class);
            setCreatorId.invoke(p, new Date());
        } catch (NoSuchMethodException e) {
            log.info("model类中不存在updateTime属性");
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        return gateway.updateById(p);
    }

    /**
     * Qry转换QueryWrapper
     *
     * @param q
     * @return
     * @param <Q>
     */
    protected  <Q extends Qry> QueryWrapper<P> convert(Q q) {

        return convert(BeanUtil.beanToMap(q));
    }

    protected QueryWrapper<P> convert(Map<String, Object> map) {

        QueryWrapper<P> queryWrapper = new QueryWrapper<>();
        List<String> fieldNames = Arrays.stream(modelClass.getDeclaredFields()).map(field -> field.getName()).collect(Collectors.toList());
        map.forEach((key, value) -> {
            if (fieldNames.contains(key) && value != null) {
                String column = StrUtil.toSymbolCase(key, StrUtil.C_UNDERLINE);
                // 如果value为list，则范围查找，注意list的为有序集合升序，size=2
                if (value instanceof List) {
                    queryWrapper.ge(column, ((List<?>) value).get(0)).le(column, ((List<?>) value).get(1));
                } else if (!StrUtil.isEmptyIfStr(value)) {
                    queryWrapper.eq(column, value);
                }
            }
        });

        return queryWrapper;
    }

    protected Class<D> currentDTOClass() {
        return (Class<D>) ReflectionKit.getSuperClassGenericType(this.getClass(), Entity.class, 0);
    }

    protected Class<P> currentModelClass() {
        return (Class<P>) ReflectionKit.getSuperClassGenericType(this.getClass(), Entity.class, 1);
    }
}
